import { Hook } from './hook';
import { Injectable, Inject } from '../decorators/inject';
import { paramOptimize, catchErr, catchReturn } from '../utils/decorates';
import { TableDatas } from './table-datas';
import { Config } from './config';
import { AddHook } from '../decorators/add-hook';
import { ImageLoader, drawImage } from '../utils/draw-helper/tools';
@Injectable('Cache')
export class Cache {
    protected rowClipPosition: Map<number, [number, number]> = new Map();
    protected colClipPosition: Map<number, [number, number]> = new Map();
    protected rowPositions: Map<number, [number, number]> = new Map();
    protected colPositons: Map<number, [number, number]> = new Map();
    protected imageCache: Map<string, HTMLImageElement> = new Map();
    public readonly colBoundary: {start: number, end: number} = {start: 0, end: 0};
    public readonly rowBoundary: {start: number, end: number} = {start: 0, end: 0};
    protected _freezePosition = {
        row: 0,
        col: 0
    }
    public get freezePosition() {
        return {...this._freezePosition};
    }
    constructor(
        protected hook: Hook,
        protected tableData: TableDatas,
        protected conf: Config
    ) {

    }
    public init() {
        this.hook.beforeRenderColHeader.add(() => {
            this.colPositons.clear();
            this.colClipPosition.clear();
            // @ts-ignore
            this.colBoundary = {start: 0, end: 0};
        });
        this.hook.beforeRenderRowHeader.add(() => {
            this.rowPositions.clear();
            this.rowClipPosition.clear();
            // @ts-ignore
            this.rowBoundary = {start: 0, end: 0};
        });
        this.hook.afterRenderColHeader.add(() => {
            // console.log(this);
        })
    }
    public updateFreezeRowPosition(y: number) {
        this._freezePosition.row = y;
    }
    public updateFreezeColPosition(x: number) {
        this._freezePosition.col = x;
    }
    public setRowBoundary(start: number, end: number) {
        // @ts-ignore
        this.rowBoundary = {start, end};
    }
    public setColBoundary(start: number, end: number) {
        // @ts-ignore
        this.colBoundary = {start, end};
    }
    public clear() {
        this.rowPositions.clear();
        this.colPositons.clear();
    }
    public setColPosition(col: number, startX: number, endX: number) {
        this.colPositons.set(col, [startX, endX]);
    }
    public setRowPostion(row: number, startY: number, endY: number) {
        this.rowPositions.set(row, [startY, endY]);
    }
    public getRowPositon(row: number) {
        if(this.rowPositions.has(row)) {
            return this.rowPositions.get(row);
        } else {
            if(row > this.rowBoundary.end) {
                let y = this.rowPositions.get(this.rowBoundary.end)[1];
                let lastY = y;
                for(let i = this.rowBoundary.end + 1; i < row; i++) {
                    lastY = y;
                    y += this.tableData.getRowHeight(i);
                }
                this.rowPositions.set(row, [lastY, y]);
                return [lastY, y];
            }
            if(row < this.rowBoundary.start) {
                let y = this.getRowPositon(this.rowBoundary.start)[0] as number;
                let lastY = y;
                for(let i = row; i < this.rowBoundary.start; i++) {
                    lastY = y;
                    y -= this.tableData.getRowHeight(i);
                }
                this.rowPositions.set(row, [lastY, y]);
                return [y, lastY]
            }
            return [0,0];
        }
    }
    // @catchReturn((col: any, result: any) => {
    //     if(col === 0) {
    //         console.log('result',result)
    //     }
    // })
    public getColPosition(col: number) {
        if(this.colPositons.has(col)) {
            return this.colPositons.get(col);
        } else {
            if(col > this.colBoundary.end) {
                let x = this.colPositons.get(this.colBoundary.end)[0];
                let lastX = x;
                for(let i = this.colBoundary.end + 1; i < col; i++) {
                    lastX = x;
                    x += this.tableData.getColWidth(col);
                }
                this.colPositons.set(col, [lastX, x]);
                return [lastX, x];
            }
            if(col < this.colBoundary.start) {
                let x = this.getColPosition(this.colBoundary.start)[0] as number;
                let lastX = x;
                for(let i = col; i < this.colBoundary.start; i++) {
                    lastX = x;
                    x -= this.tableData.getColWidth(col); 
                }
                this.colPositons.set(col, [lastX, x]);
                return [x, lastX];
            }
            return [0,0];
        }
    }
    public getCellPosition1(row: number, col: number) {
        if(!this.rowPositions.has(row) || this.colPositons.has(col)) {
            return null;
        }
        let rowPosition = this.rowPositions.get(row);
        let colPositon = this.colPositons.get(col);
        let CellPositionInfo = {
            start: {
                x: colPositon[0],
                y: rowPosition[0],
            },
            end: {
                x: colPositon[1],
                y: rowPosition[1]
            },
            size: {
                w: colPositon[1] - colPositon[0],
                h: rowPosition[1] - rowPosition[0]
            }
        }

        return CellPositionInfo;
    }
    public getPointCol(x: number) {
        let col = -1;
        this.colPositons.forEach((value, key) => {
            if(x > value[0] && x < value[1]) {
                col = key;
            }
        });
        if(col !== -1) return col;
        for(let col = this.colBoundary.start; col < this.colBoundary.end; col++) {
            let colPosition = this.getColPosition(col);
            if(x > colPosition[0] && x <= colPosition[1]) {
                return col;
            }
        }
        const freezeCol = this.conf.freeze.col;
        if(freezeCol !== -1 && (x > this.conf.header.row.width && x <= this._freezePosition.col + this.conf.header.row.width)) {
            for(let col = 0; col <= freezeCol; col++) {
                let colPosition = this.getColPosition(col);
                if(x > colPosition[0] && x <= colPosition[1]) {
                    return col;
                }
            }
        }
        return this.colBoundary.start;
    }
    public getPointRow(y: number) {
        for(let row = this.rowBoundary.start; row < this.rowBoundary.end + 1; row++) {
            let rowPosition = <[number, number]>this.getRowPositon(row);
            if(y > rowPosition[0] && y <= rowPosition[1]) {
                return row;
            }
        }
        const freezeRow = this.conf.freeze.row;
        if(freezeRow !== -1 && (y > this.conf.header.col.height && y <= this._freezePosition.row + this.conf.header.col.height)) {
            for(let row = 0; row <= freezeRow; row++) {
                let rowPosition = <[number, number]>this.getRowPositon(row);
                if(y > rowPosition[0] && y <= rowPosition[1]) {
                    return row;
                }
            }
        }
        return this.rowBoundary.start;
    }
    @catchErr({start: 0, end: 0})
    public getRowRangePositionInfo(startRow: number, endRow: number = startRow) {
        let [startY] = this.getRowPositon(startRow);
        let [_, endY] = this.getRowPositon(endRow);
        return {start: startY, end: endY};
    }
    @catchErr({start: 0, end: 0})
    public getColRangePositionInfo(startCol: number, endCol: number = startCol) {
        let [startX] = this.getColPosition(startCol);
        let [_, endX] = this.getColPosition(endCol);
        return {start: startX, end: endX};
    }
    @paramOptimize((start: TRange, end?: CellPosition) => {
        if(end === void 0) {
            if(start.rowspan !== undefined) {
                return [{row: start.row, col: start.col}, {row: start.row + start.rowspan - 1, col: start.col + start.colspan - 1}];
            } else {
                return [start];
            }
        } else {
            return [start, end];
        }
    })
    public getRangeRectInfo(start: CellPosition, end: CellPosition = start) {
        let xRect = this.getColRangePositionInfo(start.col, end.col);
        let yRect = this.getRowRangePositionInfo(start.row, end.row);
        let info = {
            start: {
                x: xRect.start,
                y: yRect.start
            },
            end: {
                x: xRect.end,
                y: yRect.end
            },
            w: xRect.end - xRect.start,
            h: yRect.end - yRect.start,
        }
        return info;
    }
    public getImageCathe(src: string): Promise<[null|string, HTMLImageElement]> {
        if(this.imageCache.has(src)) {
            return Promise.resolve([null,this.imageCache.get(src)]);
        } else {
            return new Promise(resolve => {
                ImageLoader(src).then((res) => {
                    if(res[0] === null) {
                        this.imageCache.set(src, res[1]);
                        resolve([null, res[1]]);
                    } else {
                        resolve([null, new Image()])
                    }
                });
            });
        }
    }

    public setRowClipPosition(row: number, startY: number, endY: number) {
        this.rowClipPosition.set(row, [startY, endY]);
    }

    public setColClipPosition(col: number, startX: number, endX: number) {
        return this.colClipPosition.set(col, [startX, endX]);
    }

    @paramOptimize((start: number, end: number) => {
        if(end === undefined) {
            end = start;
        }
        return [start, end];
    })
    public getRowClipPosition(start: number, end?: number) {
        let startPosition = 0;
        let endPosition = 0;
        if(this.rowClipPosition.has(start)) {
            startPosition = this.rowClipPosition.get(start)[0];
        } else {
            startPosition = this.getRowPositon(start)[0];
        }
        if(this.rowClipPosition.has(end)) {
            endPosition = this.rowClipPosition.get(end)[1];
        } else {
            endPosition = this.getRowPositon(end)[1];
        }
        if(startPosition === 0 || endPosition === 0) {
            let errMsg = `异常, startRow: ${start}, endRow: ${end}`
            if(__ENV__ === 'development') {
                throw new Error(errMsg);
            } else {
                console.log(errMsg);
            }
        }
        return [startPosition, endPosition];
    }

    @paramOptimize((start: number, end: number) => {
        if(end === undefined) {
            end = start;
        }
        return [start, end];
    })
    public getColClipPosition(start: number, end?: number) {
        let startPosition = 0;
        let endPosition = 0;
        if(this.colClipPosition.has(start)) {
            startPosition = this.colClipPosition.get(start)[0];
        } else {
            startPosition = this.getColPosition(start)[0];
        }
        if(this.colClipPosition.has(end)) {
            endPosition = this.colClipPosition.get(end)[1];
        } else {
            endPosition = this.getColPosition(end)[1];
        }
        if(startPosition === 0 || endPosition === 0) {
            let errMsg = `异常, startRow: ${start}, endRow: ${end}`
            if(__ENV__ === 'development') {
                throw new Error(errMsg);
            } else {
                console.log(errMsg);
            }
        }
        return [startPosition, endPosition];
    }

    public getClipRect(cellPosition: TRange) {
        let y = this.getRowClipPosition(cellPosition.row, cellPosition.row + cellPosition.rowspan - 1);
        let x = this.getColClipPosition(cellPosition.col, cellPosition.col + cellPosition.colspan - 1);
        return {
            x: x[0],
            y: y[0],
            endX: x[1],
            endY: y[1],
            w: x[1] - x[0],
            h: y[1] - y[0]
        }
    }
}